﻿namespace NEclipse
{
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Reflection;
  using System.Threading;
  using NEclipse.CoreLib;

  /// <summary>
  /// Class which handles all plugins of the environment.
  /// </summary>
  public class PluginLoader
  {
    /// <summary>
    /// List of all perspectives found in the environment.
    /// </summary>
    private readonly List<Type> perspecitves = new List<Type>();

    /// <summary>
    /// List of all views found in the environment.
    /// </summary>
    private readonly List<Type> views = new List<Type>();

    /// <summary>
    /// Gets the perspectives.
    /// </summary>
    public IEnumerable<Type> Perspectives 
    { 
      get
      {
        return this.perspecitves;
      }
    }

    /// <summary>
    /// Gets the views.
    /// </summary>
    public IEnumerable<Type> Views
    {
      get
      {
        return this.views;
      }
    }

    /// <summary>
    /// Loads the plugins from the specified search directory and its sub directories.
    /// </summary>
    /// <param name="searchDirectory">The search directory.</param>
    public void LoadPlugins(string searchDirectory)
    {
      this.LoadPluginsInternal(searchDirectory);
    }

    /// <summary>
    /// Loads the plugins internal.
    /// </summary>
    /// <param name="searchDirectory">The search directory.</param>
    private void LoadPluginsInternal(object searchDirectory)
    {
      List<Thread> searchThreads = new List<Thread>();
      foreach (string directory in Directory.GetDirectories(searchDirectory.ToString()))
      {
        Thread t = new Thread(new ParameterizedThreadStart(this.LoadPluginsInternal));
        t.Start(directory);
        searchThreads.Add(t);
      }

      foreach (string file in Directory.GetFiles(searchDirectory.ToString(), "NEclipse.*.dll"))
      {
        Thread t = new Thread(new ParameterizedThreadStart(this.LoadPlugin));
        t.Start(file);
        searchThreads.Add(t);
      }

      foreach (Thread t in searchThreads)
      {
        t.Join();
      }
    }

    /// <summary>
    /// Loads the plugin from the specified assembly file.
    /// </summary>
    /// <param name="file">The assembly file.</param>
    private void LoadPlugin(object file)
    {
      Assembly assembly = Assembly.Load(AssemblyName.GetAssemblyName(file.ToString()));
      foreach (Type t in assembly.GetTypes())
      {
        if (t.GetInterface(typeof(IPerspective).Name) != null)
        {
          if (this.perspecitves.Contains(t) == false)
          {
            lock (this.perspecitves)
            {
              this.perspecitves.Add(t);
            }
          }
        }

        if (t.GetInterface(typeof(IView).Name) != null)
        {
          if (this.views.Contains(t) == false)
          {
            lock (this.views)
            {
              this.views.Add(t);
            }
          }
        }
      }
    }
  }
}
